<?php


namespace app\cms\community\lib\saas;


use iflow\Utils\basicTools;

class buildNginxConfig
{
    // SSL证书文件
    protected array $ssl = [];
    protected string $serverName = 'localhost';

    // 监听端口
    protected string $listen = "80";

    // SSL配置
    protected string $sslConfig = "";

    // nginx配置
    protected string $nginxConf = "";

    // 项目根目录地址
    protected string $root = '';
    protected string $config = "";

    protected string $serverIp = '127.0.0.1';
    protected int $serverPort  = 8080;

    protected string $nginxPath;

    protected string $nginxPHPCGIConfig = '
        location / {
            root %s;
            index index.html index.php;
            if (!-e $request_filename) {
                rewrite  ^(.*)$  /index.php/$1  last;
                break;
            }
        }
    
        location ~ \.php($|/) {
            root           %s;
            fastcgi_pass   127.0.0.1:9000;
            fastcgi_index  index.php;
            fastcgi_split_path_info ^(.+\.php)(.*)$;
            fastcgi_param  SCRIPT_FILENAME  $DOCUMENT_ROOT$fastcgi_script_name; #scripts
            fastcgi_param  PATH_INFO  $fastcgi_path_info;
            fastcgi_param  PATH_TRANSLATED  $document_root$fastcgi_path_info;
            include        fastcgi_params;
        }
    ';

    protected string $nginxSwooleConfig = '
        location / {
            proxy_pass http://%s:%s;
        }
    ';

    protected string $saasServer = '';

    public function __construct()
    {
        $this->nginxConf = file_get_contents(
            app() -> getRootPath() . 'bin' .DIRECTORY_SEPARATOR . 'nginx_tmp.conf'
        );
        $this->nginxPath = config('app@nginxPath');
        $this->saasServer = config('app@saasServer');

        // 设置应用根目录
        $this->root = app() -> getRootPath() . 'public';
    }

    /**
     * 编译生成 配置文件
     * @return $this
     */
    public function buildConf(): static
    {
        if (count($this->ssl) > 0) {
            $this->listen = '443 ssl http2';
            // 设置 SSL配置
            $this->sslConfig = sprintf('
                add_header Strict-Transport-Security "max-age=31536000";
                ssl_certificate %s;
                ssl_certificate_key %s;
                ssl_protocols     TLSv1.2 TLSv1.3;
                ssl_prefer_server_ciphers on;
                ssl_ciphers EECDH+CHACHA20:EECDH+CHACHA20-draft:EECDH+AES128:RSA+AES128:EECDH+AES256:RSA+AES256::!MD5;
                ssl_session_timeout	5m;
            ', $this->ssl['ssl_certificate'], $this->ssl['ssl_certificate_key']);
        }


        $serverBindParams = $this->saasServer === 'nginx' ? [
            $this->root, $this->root
        ] : [
            $this->serverIp, $this->serverPort
        ];

        $serverConfig = sprintf(
            $this->saasServer === 'nginx' ? $this->nginxPHPCGIConfig : $this->nginxSwooleConfig,
            ...$serverBindParams
        );

        $this->config = sprintf(
            $this->nginxConf, ...[
                $this->listen,
                $this->serverName,
                $this->sslConfig,
                $serverConfig
            ]
        );
        return $this;
    }

    /**
     * 将配置文件存入 vhost目录下
     * @param string $conf
     * @return $this
     */
    public function saveConfig(string $conf = "conf" . DIRECTORY_SEPARATOR): static
    {
        $vhostPath = $this->nginxPath . DIRECTORY_SEPARATOR . $conf . 'vhost';
        // 创建目录
        !is_dir($vhostPath) && mkdir($vhostPath);
        $savePath = $vhostPath. DIRECTORY_SEPARATOR . $this->serverName . '.conf';
        // 当存在配置文件时 删除
        file_exists($savePath) && unlink($savePath);
        file_put_contents(
            $savePath,
            $this->config
        );
        return $this;
    }

    /**
     * 重启nginx服务
     * @return bool
     */
    public function reload(): bool
    {
        if (!file_exists($this->nginxPath . DIRECTORY_SEPARATOR . 'sbin/nginx')) {
            return false;
        }
        $run = (new basicTools()) -> execShell(
            $this->nginxPath . DIRECTORY_SEPARATOR . 'sbin/nginx -s reload'
        );
        return $run === '';
    }

    /**
     * @param string $serverName
     * @return $this
     */
    public function setServerName(string $serverName): static
    {
        $this->serverName = $serverName;
        return $this;
    }

    /**
     * @param string $root
     * @return $this
     */
    public function setRoot(string $root): static
    {
        $this->root = $root;
        return $this;
    }

    /**
     * 存储SSL证书
     * @param array $ssl
     * @return static
     */
    public function setSsl(array $ssl): static
    {
        if (
            empty($ssl['ssl_certificate'])
            || empty($ssl['ssl_certificate_key'])
        ) return $this;

        $root = $this->nginxPath . "ssl/{$this -> serverName}";
        !is_dir($root) && mkdir($root);
        foreach ($ssl as $key => $value) {
            $savePath = $root . DIRECTORY_SEPARATOR . $key;
            file_exists($savePath) && unlink($savePath);
            file_put_contents($savePath, $value);
            $ssl[$key] = $savePath;
        }

        $this->ssl = $ssl;
        return $this;
    }

    /**
     * @return string
     */
    public function getConfig(): string
    {
        return $this->config;
    }

    /**
     * @param mixed|string $saasServer
     * @return static
     */
    public function setSaasServer(mixed $saasServer): static
    {
        $this->saasServer = $saasServer;
        return $this;
    }

    /**
     * @param string $serverIp
     * @return $this
     */
    public function setServerIp(string $serverIp): static
    {
        $this->serverIp = $serverIp;
        return $this;
    }

    /**
     * @param int $serverPort
     * @return $this
     */
    public function setServerPort(int $serverPort): static
    {
        $this->serverPort = $serverPort;
        return $this;
    }

    /**
     * 手动设置Nginx配置
     * @param string $config
     * @return $this
     */
    public function setConfig(string $config): static
    {
        $this->config = $config;
        return $this;
    }
}